
tap下集實作2:scan - countdown為例子來觀察一下。import { interval, map, scan, filter, tap } from 'rxjs';
// get element
const countdown = document.getElementById('countdown');
const message = document.getElementById('message');
// final countdown
console.log(' === case4: 模擬在pipe途中,使用tap修改side effect ===');
const finalCountdown$ = interval(1000).pipe(
map((val) => 1),
scan((accum, current) => accum - current, 6),
filter((val) => val >= 0),
tap((val) => {
// (2)利用tap在pipe途中,進行side effect的設定,由於我們沒有修改stream的值,故不影響!
countdown.innerHTML = `${val}`;
if (!val) message.innerHTML = 'HAPPY NEW YEAR!!';
}),
map((val) => val + 10) //<-- (3)模擬額外的動作
);
// show on console
finalCountdown$.subscribe(console.log);
//(1) 取消使用subscribe設定side effect
// modify HTML
// finalCountdown$.subscribe((val) => {
// countdown.innerHTML = `${val}`;
// if (!val) message.innerHTML = 'HAPPY NEW YEAR!!';
// });
我們原本於subscribe中進行HTML side effect的設定,現在做些修正:
comment掉(如(1)),取消使用subscribe設定side effecttap之中進行side effect的修改map模擬後續動作,來驗證tap可於任意位置進行side effect的設定
HTML可以在tap中進行修改,且不影響我們後續map的輸出,超方便的!tap(observer)也是被允許的,我們延續剛剛的例子,順勢地建立出next及complete的func,來看看有什麼有趣的情形☕ ...
tap({
next: (val) => {
// (2)利用tap在pipe途中,進行side effect的設定,由於我們沒有修改stream的值,故不影響!
countdown.innerHTML = `${val}`;
if (!val) message.innerHTML = 'HAPPY NEW YEAR!!';
},
complete: () => console.log('completed!'), //<-- 注意complete何時被觸發
}),
...

observer卻始終沒有觸發complete,這倒底是什麼原因??tap((val)=>console.log(val))看看,唉呦,都用了filter了,數據怎一直冒出來!!??這不是弄糊塗我了嗎??const finalCountdown$ = interval(1000).pipe(
map((val) => 1),
scan((accum, current) => accum - current, 6),
tap((val) => console.log('before-filter:', val)), //<-- tap放在此觀察看看,數據仍不斷出現
filter((val) => val >= 0),
...
map((val) => val + 10)
);

這原因就是,filter只幫我們過濾數據到後方,但並未中止源頭的observable,因此,interval持續地發送數據,也因此tap(observer)的complete不會被觸發!✍
這也是為什麼,學習RxJS常看完說明之後,但在實作時卻又常摸不著頭緒(就跟學妹一樣,明明牽了小手,卻又說~"學長~我想我們只是朋友"),所以建議還是親手體驗,才能邁向天長地久XD。
tap可以協助我們執行side effect,設定HTML格式或發出錯誤訊息。tap(observer)是被允許執行的。observer裡的complete不被觸發,是因為filter的特性。好啦! 完成第16天的任務啦,超越50%,再加把勁,繼續往終點前進!!